package fr.ece.ing4.si.montecarlo;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;


public class monteCarloThread{
	
	private String callPutFlag;
	private double s ;
	private double x ;
	private double T ;
	private double r ;
	private double b ;
	private double v ;
	private int nSteps ;
	private int nSimulations ; 
	private double dt;
	private double drift; 
	private double vSqrdt;
	private double sum;
	private int z;
	private double result;

	public monteCarloThread(String callPutFlag, double s,	double x, double T, double r, double b,	
			double v, int nSteps, int nSimulations)
	{
		this.callPutFlag= callPutFlag;
		this.s=s;
		this.x=x;
		this.T=T;
		this.r=r;
		this.b=b;
		this.v=v;
		this.nSteps=nSteps;
		this.nSimulations=nSimulations;
   	}
	

	//Method to load 6 parameters from the text files
	//s,x,T,r,b,v
	public static String[] readParameters() throws IOException {
		
		final BufferedReader reader = 
			      new BufferedReader(new FileReader("params.txt"));
	    String stockInfo = null;
	    String[] params = null;
		while((stockInfo = reader.readLine()) != null) {
	    	params = stockInfo.split(",");
	    }
	    reader.close();
	    return params;    
	  }

	
	public double task()
	{
		Random rand = new Random();
		double st = this.s; 
		for (int j=1; j<= nSteps; j++){
			st = st * Math.exp(drift + vSqrdt * rand.nextGaussian()); 
		}
		return Math.max(z*(st-this.x), 0);
 	}
	
	//Multithreaded version
	public double computePremium()
		    throws IOException, InterruptedException, ExecutionException {  
		
				//Set the primary values
				dt = this.T/ this.nSteps;
				drift = (this.b - (this.v*this.v) / 2) * dt; 
				vSqrdt = this.v * Math.sqrt(dt);
				z=0;
				if (this.callPutFlag =="c"){
					z=1; 
				}
				else if (this.callPutFlag =="p"){
					z=-1; 
				}
				//Random rand = new Random();
				sum = 0;
				//Set the number of threads, analyzing the number of processor cores and 
				//checking the blocking coefficient. 
				final int numberOfCores = Runtime.getRuntime().availableProcessors();
			    final double blockingCoefficient = 0.2;
			    final int poolSize = (int)(numberOfCores / (1 - blockingCoefficient));
			    final List<Callable<Double>> partitions = 
			    	      new ArrayList<Callable<Double>>();
				//Inside the main FOR, we add this processes to the the list of callables.
			    for(int i=1; i<= nSimulations; i++) {
			        partitions.add(new Callable<Double>() {
			          public Double call() throws Exception {
			            return task();
			          }        
			        });
			      }
			    final ExecutorService executorPool = 
					      Executors.newFixedThreadPool(poolSize);    
					    final List<Future<Double>> valueOfSums = 
					      executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);
					    
					    for(final Future<Double> valueOfASum : valueOfSums)  
					      sum += valueOfASum.get(); 

					    executorPool.shutdown();
					    result = (Math.exp(-this.r*this.T)) * (sum/this.nSimulations) ;
					    return result; 
		  } 
}
